#ifndef _COMP_H
#define _COMP_H

#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <fstream>

#include "globals.h"
#include "bitops.h"
#include "Charnode.h"


using namespace std;


template<class TYPE>
class CompressionWriting
{
	map<TYPE, string> huffmanTable;
	Charnode<TYPE> *huffmanTreeRoot;
	string outputFilename;
	string inputFilename;

	map<TYPE, int> freqMap;

private:
	int convertStringToBitPattern(string str);
	int totalNumOfBits(void);

public:
	CompressionWriting(){}

	CompressionWriting(Charnode<TYPE> *root, map<TYPE, string> table, map<TYPE, int> freqMap, string oname, string iname);

	void writeCompressedDataToFile();

	void displayOutputFile();

	void writeHuffmanTreeBitPattern(Charnode<TYPE> *tree, obstream &outfile);
};



template<class TYPE>
CompressionWriting<TYPE>::CompressionWriting(Charnode<TYPE> *root, map<TYPE, string> table, map<TYPE, int> freMap, string oname, string iname)
{
	huffmanTreeRoot = root;
	huffmanTable = table;
	outputFilename = oname;
	inputFilename = iname;

	freqMap = freMap;
}

template<class TYPE>
void CompressionWriting<TYPE>::writeCompressedDataToFile()
{
	LOG("\nWriting Pattern: \n");

	ibstream infile(inputFilename.c_str());
	obstream outfile(outputFilename.c_str());

	outfile.writebits(BITS_PER_INT, freqMap.size());			//Writing Number of unique Characters
	writeHuffmanTreeBitPattern(huffmanTreeRoot, outfile);		//Writing Huffman Tree

	outfile.writebits(BITS_PER_INT, totalNumOfBits());			//Writing Total Number of Bits to be compressed

	//Writing Compressed Data

	int inbits;
	infile.rewind();
	while (infile.readbits(BITS_PER_WORD, inbits)) 
	{
		//cout << (TYPE)inbits << " = " << huffmanTable[(TYPE)inbits];

		int bitPattern = convertStringToBitPattern(huffmanTable[(TYPE)inbits]);

		//cout << "     =    " << bitPattern << endl;

		outfile.writebits(huffmanTable[(TYPE)inbits].length(), bitPattern);
	}

	outfile.flushbits();
	infile.close();
	outfile.close();
}


template <class TYPE>
int CompressionWriting<TYPE>::totalNumOfBits()
{
	int count = 0;
	int n = freqMap.size();

	for(map<char, int>::iterator ii = freqMap.begin(); ii != freqMap.end(); ++ii)
	{
		//Length of each character code * num_of_times the char appears of all the chars = num of bits in the character
		count += huffmanTable[(*ii).first].length() * (*ii).second;		
	}

	LOG("Count = " << count << endl);
	return count;
}



template<class TYPE>
int CompressionWriting<TYPE>::convertStringToBitPattern(string str)
{
	int bitPattern = 0;
	int n = str.length();
	for(int i=0; i<n; i++)
		bitPattern += (1 << (n-i-1)) * (str[i] - '0');

	return bitPattern;
}

template<class TYPE>
void CompressionWriting<TYPE>::displayOutputFile()
{
	ibstream infile(outputFilename.c_str());
	ofstream outfile("xxx");

	cout << "\nDisplaying Output File: " << endl;
	int inbits;
	while(infile.readbits(1, inbits) != false)
	{
		cout << inbits;
		outfile << inbits;
	}

	outfile.close();
}

template<class TYPE>
void CompressionWriting<TYPE>::writeHuffmanTreeBitPattern(Charnode<TYPE> *node, obstream &outfile)
{
	if(node == NULL)
		return;

	if(node->GetLeft() == NULL && node->GetRight() == NULL)
	{
		outfile.writebits(1, 1);
		outfile.writebits(BITS_PER_WORD, node->GetChar());
	}

	else
	{
		outfile.writebits(1, 0);
		writeHuffmanTreeBitPattern(node->GetLeft(), outfile); 
		writeHuffmanTreeBitPattern(node->GetRight(), outfile); 
	}
}













#endif
